extern crate rand;

use self::rand::thread_rng;
use self::rand::Rng;

pub type Grid = Vec<Vec<i32>>;
// Layout:
// y[x][]
//
// 3
// 2
// 1
// 0 1 2 3 

pub trait TetGrid {
    fn expand(_length:i32) ->Vec<Vec<i32>>{
        Vec::new()
    }
    fn transpose(&self) -> Vec<Vec<i32>> {
        Vec::new()
    }
}

impl TetGrid for Grid {
    fn expand(length:i32)-> Vec<Vec<i32>> {
        let mut col:Vec<Vec<i32>> = Vec::new();
        let mut row:Vec<i32> = Vec::new();
        for _i in 0 .. length {
            row.push(0);
        }
        for _i in 0 .. length {
            col.push(row.clone())
        }
        col


    }
    fn transpose(&self) -> Vec<Vec<i32>> {
        let mut transposed:Vec<Vec<i32>> = self.clone();
        for i in 0 .. self.len() {
            for j in 0 .. self.len() {
                transposed[j][i]=self[i][j]
            }
        }
        return transposed
    }

}

pub fn main_grid() -> Grid {

    let mut x:Vec<i32> = Vec::new();
    for _i in 0 .. 16 {
        x.push(0);
    }
    let mut board = Grid::new();
    for _i in 0 .. 24 {
        board.push(x.clone());
    }

    board
}

//this is only to be used for drawing purposes

pub fn overlay_grid(tet: &Grid, mut board: Grid, new_pos:(usize, usize)) -> Grid {
    // Board needs whitespace to left and below
    
    let x = new_pos.1;
    let y = new_pos.0;

    for i in 0 .. tet.len() {
        for j in 0 .. tet[i].len() {
            if (tet[i][j] != 0) {
                if (board[i+y][j+x] != 0) {
                    panic!("Writing onto already drawn block")
                }
                board[i+y][j+x] = tet[i][j];
            }
        }
    }


    board
}

pub fn eval_over(tet_reference: &Grid, board_reference: &Grid, y_new:usize, x_new:usize) -> (bool) {
    // Before a tet can be moved, check its new location

    let tet = tet_reference.to_vec();
    let board = board_reference.to_vec();

    let x = x_new;
    let y = y_new;

    let mut valid:bool = true;

    for i in 0 .. tet.len() {
        for j in 0 .. tet[i].len() {
            if tet[i][j] == 0 {
                //
            }
            else if (i + y < 3 || j + x < 3 || j + x > board[0].len() - 2 || i + y > board.len() - 2) {
                //println!("Index is off grid!");
                valid = false;
                 
            }
            // Check for overlaps with other blocks
            else if board[i+y][j+x] != 0 {
                valid = false;
            }

        }
    }

//                println!("By {}, bx {},  tlen {}", board.len(), board[0].len(), tet.len());
    valid
}


pub fn generate_bag() -> Vec<usize> {
// Generates list of the next 7 pieces

// 1=I, 2=J, 3=L, 4=O, 5=s, 6=T, 7=Z

    let mut bag:Vec<usize> = Vec::new();
    let mut contents:Vec<usize> = vec![0,1,2,3,4,5,6];
    while contents.len() > 0 { 
        let mut rng = thread_rng();
        let x = rng.gen_range(0, contents.len());
        bag.push(contents[x]);
        contents.remove(x);
    }
    return bag
}


pub fn prep_tets() -> Vec<Grid> {
    let mut output:Vec<Grid> = Vec::new();
    for i in 0 .. 7 {
        output.push(build_tet(i));
    }
    output
}


pub fn build_tet(tet_type:usize) -> Grid {
    let tet_coords = [
        
        (4, [(2,0), (2,1), (2,2), (2,3)]),    // I
        (3, [(1,0), (1,1), (1,2), (2,0)]),    // J
        (3, [(1,0), (1,1), (1,2), (2,2)]),    // L
        (2, [(0,0), (0,1), (1,0), (1,1)]),    // O
        (3, [(0,1), (0,2), (1,0), (1,1)]),    // S
        (3, [(0,1), (1,0), (1,1), (1,2)]),    // T
        (3, [(0,0), (0,1), (1,1), (1,2)])     // Z
    ];

    let mut tet:Grid = Grid::expand(tet_coords[tet_type].0);
    for i in tet_coords[tet_type].1.iter() {
        tet[i.0][i.1] = (tet_type + 1) as i32;
    }
    tet

}

pub fn place_new_tet(board:&Grid, tet:&Grid) -> (usize, usize){
    let mut x = (board[0].len() - tet.len()) / 2;
    if board[0].len() % x != 0 {
        x +=1; 
    }
    let y = board.len() - tet.len();

    (y, x)
}


pub fn rotate_left(tet_ref:&Grid) -> Grid {
    let mut new_tet:Grid = Grid::new();
    let mut tet = tet_ref.to_vec();

    // flip vertically

    while tet.len() != 0 {
        let x = tet.pop();
        if x.is_some() {
            new_tet.push(x.unwrap());
        }
    }
    new_tet.transpose()

}


pub fn rotate_right(tet_ref:&Grid) -> Grid {
    // horizontally flip the grid
    let mut tet = tet_ref.to_vec();
    let mut x = tet.len();
    let mut new_tet:Grid = Vec::new();
    for i in 0 .. x {
        new_tet.push(Vec::new());
        for j in 0 .. x {
            let n = tet[i].pop();
            if n.is_some() {
                new_tet[i].push(n.unwrap());
            }
        } 
    }
    new_tet.transpose()
}

pub fn move_tet(tet_ref :&Grid, board_ref: &Grid, offsets:(i32, i32), yx_pos: (usize, usize)) -> (usize, usize) {
    let x = (yx_pos.1 as i32 + offsets.1);
    let y = (yx_pos.0 as i32 + offsets.0);

    if eval_over(tet_ref, board_ref, y as usize, x as usize) {
        (y as usize, x as usize)
    }
    
    else {
        yx_pos
    }
    
}

pub fn find_filled(board: &Grid) -> Vec<usize> {
    let mut filled:Vec<usize> = Vec::new();
    for i in 3 .. board.len() {
        let mut found = true;
        for j in 3 .. board[i].len() -1 {
            if board[i][j] == 0 {
                found = false;
            }
        }
        if found {
            filled.insert(0, i);
        }
    }
    filled
}

